grammar Calc;

options {
    k=1;                                // LL(1) - do not use LL(*)
    language=Java;                      // target language is Java (= default)
    output=AST;                         // build an AST
}

tokens {
    COLON       =   ':'     ;
    SEMICOLON   =   ';'     ;
    LPAREN      =   '('     ;
    RPAREN      =   ')'     ;
    KOMMA       =   ','     ;

    // operators
    BECOMES     =   ':='    ;
    PLUS        =   '+'     ;
    MINUS       =   '-'     ;
    TIMES       =   '*'     ;
    DIVIDEBY    =   '/'     ;
    
    // equality
    LESSTHAN    =   '<'     ;
    LESSEQUAL   =   '<='    ;
    GREATERTHAN =   '>'     ;
    GREATEREQUAL=   '>='    ;
    EQUAL       =   '=='    ;
    NOTEQUAL    =   '!='    ;
    

    // keywords
    SWAP        =   'swap'      ;
    PROGRAM     =   'program'   ;
    VAR         =   'var'       ;
    PRINT       =   'print'     ;
    INTEGER     =   'integer'   ;
    IF          =   'if'        ;
    THEN        =   'then'      ;
    ELSE        =   'else'      ;
    DO          =   'do'        ;
    WHILE       =   'while'     ;
}

@lexer::header {
package vb.week3.calc;
}

@header {
package vb.week3.calc;
}

// Parser rules

program
    :   statementsanddeclarations EOF
            ->  ^(PROGRAM statementsanddeclarations)
    ;
    
statementsanddeclarations
    :   ((declaration SEMICOLON!)* (statement SEMICOLON!))*
    ;
    
declarations
    :   (declaration SEMICOLON!)*
    ;
    
statements
    :   (statement SEMICOLON!)+
    ;

declaration
    :   VAR^ IDENTIFIER COLON! type
    ;
    
statement
    :   compound_expr
    |   print_stat
    |   swap_stat
    |   dowhile_stat
    ;
    
print_stat
    :   PRINT^ LPAREN! compound_expr RPAREN!
    ;

swap_stat
    :   SWAP^ LPAREN! IDENTIFIER KOMMA! IDENTIFIER RPAREN!
    ;

dowhile_stat
    :   DO^ statements WHILE compound_expr
    ;

compound_expr
  : simpel_expr (BECOMES^ compound_expr)?
  ;
  
simpel_expr
  : expr0
  | IF^ expr0 THEN! expr0 ELSE! expr0
  ;
  
expr0
  :   expr1 ((LESSTHAN^ | LESSEQUAL^ | GREATERTHAN^ | GREATEREQUAL^ | EQUAL^ | NOTEQUAL^) expr1)?
  ;
  
expr1
    :   expr2 ((PLUS^ | MINUS^) expr2)*
    ;

expr2
    :   operand ((TIMES^ | DIVIDEBY^) operand)*
    ;
    
operand
    :   IDENTIFIER
    |   NUMBER
    |   LPAREN! compound_expr RPAREN!
    ;

lvalue
    :   IDENTIFIER
    ;
    

type
    :   INTEGER
    ;


// Lexer rules

IDENTIFIER
    :   LETTER (LETTER | DIGIT)*
    ;

NUMBER
    :   DIGIT+
    ;


COMMENT
    :   '//' .* '\n' 
            { $channel=HIDDEN; }
    ;

WS
    :   (' ' | '\t' | '\f' | '\r' | '\n')+
            { $channel=HIDDEN; }
    ;

fragment DIGIT  :   ('0'..'9') ;
fragment LOWER  :   ('a'..'z') ;
fragment UPPER  :   ('A'..'Z') ;
fragment LETTER :   LOWER | UPPER ;

// EOF

